/*
 * Copyright 2010 David de Mingo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.shalma.internal.client;

import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.shalma.internal.reflection.Attribute;
import org.shalma.internal.reflection.MetaClass;
import org.shalma.internal.reflection.MetaClassRegistry;

import com.google.gwt.user.client.ui.SuggestOracle;

public class SearchOracle extends SuggestOracle {

	private static final String ATTRIBUTE_SEPARATOR = "-";

	private TreeMap<String, MetaClass> entityMetaClasses;

	public SearchOracle() {

		entityMetaClasses = new TreeMap<String, MetaClass>();

		for (Entry<String, MetaClass> entry : MetaClassRegistry.instance
				.getMetaClasses().entrySet()) {

			MetaClass metaClass = entry.getValue();
			if (metaClass.isEntity()) {
				entityMetaClasses.put(metaClass.getSimpleName().toLowerCase(),
						metaClass);
			}
		}
	}

	@Override
	public void requestSuggestions(Request request, Callback callback) {

		ArrayList<Suggestion> suggestions = new ArrayList<SuggestOracle.Suggestion>();

		String query = request.getQuery().toLowerCase().trim();

		int index = query.indexOf('.');
		if (index != -1) {
			if (query.indexOf('.', index + 1) != -1)
				return;

			MetaClass metaClass = entityMetaClasses.get(query.substring(0,
					index));
			selectAttribute(suggestions, metaClass, query.substring(index + 1));

		} else {
			selectClass(request, suggestions, query);
		}

		callback.onSuggestionsReady(request, new Response(suggestions));
	}

	private void selectAttribute(ArrayList<Suggestion> suggestions,
			MetaClass metaClass, String query) {

		String prefix = metaClass.getSimpleName().toLowerCase()
				+ ATTRIBUTE_SEPARATOR;

		for (Attribute attribute : metaClass.getAttributes()) {
			String name = prefix + attribute.getName().toLowerCase();
			suggestions.add(new SearchSuggestion(name, name));
		}
	}

	private void selectClass(Request request,
			ArrayList<Suggestion> suggestions, String query) {

		if (query.equals("*")) {

			for (MetaClass bean : entityMetaClasses.values()) {
				String simpleName = bean.getSimpleName();
				suggestions.add(new SearchSuggestion(simpleName, simpleName));
				if (suggestions.size() == request.getLimit())
					break;
			}
		}

		else {
			boolean found = false;
			for (Entry<String, MetaClass> entry : entityMetaClasses.entrySet()) {
				String name = entry.getKey();
				if (name.startsWith(query)) {
					found = true;
					String simpleName = entry.getValue().getSimpleName();
					suggestions
							.add(new SearchSuggestion(simpleName, simpleName));
					if (suggestions.size() == request.getLimit())
						break;
				} else if (found)
					break;
			}
		}
	}

	public static class SearchSuggestion implements Suggestion {

		private String displayString;
		private String replacementString;

		public SearchSuggestion(String displayString, String replacementString) {
			this.replacementString = replacementString;
			this.displayString = displayString;
		}

		public String getDisplayString() {
			return displayString;
		}

		public String getReplacementString() {
			return replacementString;
		}
	}
}